#!/usr/bin/python
# -*- coding: utf8 -*-

from collections import defaultdict
from estado import EstadoMuerto

class Modelo(object):
    
    def __init__(self, poblacion):
        self.poblacion = poblacion
        self.callbacks = []

    def avanzarIteracion(self):
        p = self.poblacion
        for agente in p.agentes:
            for vecino in p.vecinos(agente):
                agente._encontrarseCon(vecino, p.peso(agente, vecino))
        
        for agente in p.agentes:
            agente._terminarIteracion()
            
        self.ejecutarCallbacks()
        self.poblacion.iteracion += 1
            
    def registrarCallbacks(self,callback):
        self.callbacks.append(callback)
        
    def ejecutarCallbacks(self):
        for each in self.callbacks:
            each(self.poblacion)
  


class Curbing(object):
    
    def __init__(self):
        self.curbeds = defaultdict(lambda:[])
        self.inicializar()
    
    def inicializar(self):
        pass
    
    def AplicarCurbing(self,poblacion):
        if self.criterioDeCurbingPoblacion(poblacion):
            self.preAplicarCurbing(poblacion)
            for each in poblacion.agentes:
                self.curbe(each,poblacion.vecinos(each),poblacion.iteracion)
            self.postAplicarCurbing(poblacion)    
    
    def preAplicarCurbing(self, poblacion):
        pass
    
    def postAplicarCurbing(self, poblacion):
        pass
    
    def curbe(self,agente,vecinos,iteracion):
        if agente.estado == EstadoMuerto:
            return
        if self.criterioDeCurbingAgente(agente,vecinos):
            self.preCurve(agente,vecinos,iteracion)
            self.curbeds[iteracion].append(agente)
            agente.cambiarEstado(EstadoMuerto)
            self.postCurve(agente,vecinos,iteracion)
    
    def preCurve(self,agente,vecinos,iteracion):
        pass
    
    def postCurve(self,agente,vecinos,iteracion):
        pass
    
    def criterioDeCurbingPoblacion(self,poblacion):
        raise NotImplementedError
    
    def criterioDeCurbingAgente(self,agente,vecinos):
        raise NotImplementedError
    

  
class Aislamiento(object):
    
    def __init__(self):
        self.aislados = defaultdict(lambda:[])
        self.desaislados = defaultdict(lambda:[])
        self.inicializar()
    
    def inicializar(self):
        pass
    
    def aplicarAislamiento(self,poblacion):
        if self.criterioDeAislamientoPoblacion(poblacion):
            self.preAplicarAislamiento(poblacion)
            for each in poblacion.agentes:
                self.aislar(each,poblacion.vecinos(each),poblacion.iteracion)
            self.preAplicarAislamiento(poblacion)    
    
    def preAplicarAislamiento(self, poblacion):
        pass
    
    def postAplicarAislamiento(self, poblacion):
        pass
    
    def aplicarDesaislamiento(self,poblacion):
        if self.criterioDeDesaislamientoPoblacion(poblacion):
            self.preAplicarDesaislamiento(poblacion)
            for each in poblacion.agentes:
                self.desaislar(each,poblacion.vecinos(each),poblacion.iteracion)
            self.preAplicarDesaislamiento(poblacion)    
    
    def preAplicarDesaislamiento(self, poblacion):
        pass
    
    def postAplicarDesaislamiento(self, poblacion):
        pass
    
    def aislar(self,agente,vecinos,iteracion):
        if self.criterioDeAislamientoAgente(agente,vecinos):
            self.preAislar(agente, vecinos, iteracion)
            self.aislados[iteracion].append(agente)
            agente.aislado = True
            self.postAislar(agente, vecinos, iteracion)    
    
    def preAislar(self, agente,vecinos,iteracion):
        pass
    
    def postAislar(self, agente,vecinos,iteracion):
        pass
        
    def desaislar(self,agente,vecinos,iteracion):
        if agente.aislado and self.criterioDeDesaislamientoAgente(agente,vecinos):
            self.preDesaislar(agente,vecinos,iteracion)
            self.desaislados[iteracion].append(agente)
            agente.aislado = False
            self.postDesaislar(agente,vecinos,iteracion)
    
    def preDesaislar(self, agente,vecinos,iteracion):
        pass
    
    def postDesaislar(self, agente,vecinos,iteracion):
        pass
    
    def criterioDeAislamientoPoblacion(self,poblacion):
        raise NotImplementedError
    
    def criterioDeAislamientoAgente(self,agente,vecinos):
        raise NotImplementedError
    
    def criterioDeDesaislamientoPoblacion(self,poblacion):
        raise NotImplementedError
    
    def criterioDeDesaislamientoAgente(self,agente,vecinos):
        raise NotImplementedError
    
class Vacuna(object):
    
    def __init__(self,tipo):
        self.tipo = tipo
        self.vacunados = defaultdict(lambda:[])
        self.inicializar()
    
    def inicializar(self):
        pass
    
    def vacunar(self,agente,vecinos,iteracion):
        if self.criterioDeVacunacionAgente(agente,vecinos):
            self.preVacunar(agente,vecinos,iteracion)
            self.vacunados[iteracion].append(agente)
            agente.vacunas.append(self)
            agente.vacunado = True
            self.postVacunar(agente,vecinos,iteracion)
            
    def preVacunar(self,agente,vecinos,iteracion):
        pass
        
    def postVacunar(self,agente,vecinos,iteracion):
        pass
        
    def criterioDeVacunacionPoblacion(self,poblacion):
        raise NotImplementedError
    
    def aplicarVacuna(self,poblacion):
        if self.criterioDeVacunacionPoblacion(poblacion):
            self.preAplicarVacuna(poblacion)
            for each in poblacion.agentes:
                self.vacunar(each,poblacion.vecinos(each),poblacion.iteracion)
            self.postAplicarVacuna(poblacion)
    
    def preAplicarVacuna(self, poblacion):
        pass
    
    def postAplicarVacuna(self, poblacion):
        pass
    
    def criterioDeVacunacionAgente(self,agente,vecinos):
        raise NotImplementedError